import 'dart:math';

import 'package:flutter/material.dart';
import 'package:wallet_utils/button_style.dart';
import 'package:wallet_utils/toast.dart';

import '../../../../color.dart';
import '../../base/brn_picker.dart';
import '../../base/brn_picker_title.dart';
import '../brn_date_time_formatter.dart';
import '../../config/brn_picker_config.dart';
import '../../config/brn_picker_title_config.dart';
import '../../constants/brn_date_picker_constants.dart';
import '../../i18n/brn_date_picker_i18n.dart';
import 'package:flutter_screenutil/flutter_screenutil.dart';

enum ColumnType { Year, Month, Day }

/// 初始化选择日期默认值
enum RangeInitType { start, end }

/// Solar months of 31 days.
const List<int> _solarMonthsOf31Days = const <int>[1, 3, 5, 7, 8, 10, 12];

/// DatePicker widget.

// ignore: must_be_immutable
class BrnDateRangeWidget extends StatefulWidget {
  BrnDateRangeWidget({
    Key? key,
    required this.minDateTime,
    required this.maxDateTime,
    required this.initialStartDateTime,
    required this.initialEndDateTime,
    this.dateFormat = DATETIME_PICKER_DATE_FORMAT,
    this.locale = DATETIME_PICKER_LOCALE_DEFAULT,
    required this.pickerTitleConfig,
    this.onCancel,
    // this.onChange,
    this.onConfirm,
    this.canPop = true,
    required this.themeData,
    this.rangeInitType = RangeInitType.start,
  }) : super(key: key) {
    DateTime minTime = minDateTime ?? DateTime.parse(DATE_PICKER_MIN_DATETIME);
    DateTime maxTime = maxDateTime ?? DateTime.parse(DATE_PICKER_MAX_DATETIME);
    assert(minTime.compareTo(maxTime) < 0);
  }

  final DateTime? minDateTime,
      maxDateTime,
      initialStartDateTime,
      initialEndDateTime;
  final String? dateFormat;
  final DateTimePickerLocale locale;
  final PickerTitleConfig pickerTitleConfig;

  final DateVoidCallback? onCancel;
  final DateRangeValueCallback? onConfirm;
  final bool canPop;
  PickerConfig themeData;

  final RangeInitType rangeInitType;

  /// 日期区间暂时不提供onChange方法，因为没有意义
  DateRangeValueCallback? onChange;
  @override
  State<StatefulWidget> createState() => _BrnDateRangeWidgetState(
      this.minDateTime,
      this.maxDateTime,
      this.initialStartDateTime,
      this.initialEndDateTime,
      this.rangeInitType);
}

class _BrnDateRangeWidgetState extends State<BrnDateRangeWidget> {
  late DateTime _minDateTime, _maxDateTime;
  late int _currYear, _currMonth, _currDay;
  late int _currStartYear, _currStartMonth, _currStartDay;
  late int _currEndYear, _currEndMonth, _currEndDay;
  late List<int> _yearRange, _monthRange, _dayRange;
  late FixedExtentScrollController _yearScrollCtrl,
      _monthScrollCtrl,
      _dayScrollCtrl;

  late Map<String, FixedExtentScrollController> _scrollCtrlMap;
  late Map<String, List<int>> _valueRangeMap;

  bool _isChangeDateRange = false;

  // DateTime? initialStartDateTime, initialEndDateTime;
  late RangeInitType _rangeInitType;

  _BrnDateRangeWidgetState(
      DateTime? minDateTime,
      DateTime? maxDateTime,
      DateTime? initialStartDateTime,
      DateTime? initialEndDateTime,
      RangeInitType rangeInitType) {
    DateTime initStartDateTime = initialStartDateTime ?? DateTime.now();
    DateTime initEndDateTime = initialEndDateTime ?? DateTime.now();
    this._rangeInitType = rangeInitType;
    // handle current selected year、month、day
    late DateTime initDateTime;
    if (rangeInitType == RangeInitType.start) {
      initDateTime = initStartDateTime;
    } else if (rangeInitType == RangeInitType.end) {
      initDateTime = initEndDateTime;
    }

    this._currYear = initDateTime.year;
    this._currMonth = initDateTime.month;
    this._currDay = initDateTime.day;

    this._currStartYear = initStartDateTime.year;
    this._currStartMonth = initStartDateTime.month;
    this._currStartDay = initStartDateTime.day;

    this._currEndYear = initEndDateTime.year;
    this._currEndMonth = initEndDateTime.month;
    this._currEndDay = initEndDateTime.day;

    // handle DateTime range
    this._minDateTime = minDateTime ?? DateTime.parse(DATE_PICKER_MIN_DATETIME);
    this._maxDateTime = maxDateTime ?? DateTime.parse(DATE_PICKER_MAX_DATETIME);

    // limit the range of year
    this._yearRange = _calcYearRange();
    this._currYear = min(max(_minDateTime.year, _currYear), _maxDateTime.year);

    // limit the range of month
    this._monthRange = _calcMonthRange();
    this._currMonth = min(max(_monthRange.first, _currMonth), _monthRange.last);

    // limit the range of day
    this._dayRange = _calcDayRange();
    this._currDay = min(max(_dayRange.first, _currDay), _dayRange.last);

    // create scroll controller
    _yearScrollCtrl =
        FixedExtentScrollController(initialItem: _currYear - _yearRange.first);
    _monthScrollCtrl = FixedExtentScrollController(
        initialItem: _currMonth - _monthRange.first);
    _dayScrollCtrl =
        FixedExtentScrollController(initialItem: _currDay - _dayRange.first);

    _scrollCtrlMap = {
      'y': _yearScrollCtrl,
      'M': _monthScrollCtrl,
      'd': _dayScrollCtrl
    };
    _valueRangeMap = {'y': _yearRange, 'M': _monthRange, 'd': _dayRange};
  }

  @override
  Widget build(BuildContext context) {
    return GestureDetector(
      child: Material(
          color: Colors.transparent, child: _renderPickerView(context)),
    );
  }

  /// render date picker widgets
  Widget _renderPickerView(BuildContext context) {
    Widget datePickerWidget = _renderDatePickerWidget();

    // display the title widget
    if (widget.pickerTitleConfig.title != null ||
        widget.pickerTitleConfig.showTitle) {
      Widget titleWidget = PickerTitle(
        pickerTitleConfig: widget.pickerTitleConfig,
        locale: widget.locale,
        onCancel: () => _onPressedCancel(),
        onConfirm: () => _onPressedConfirm(),
        themeData: widget.themeData,
      );
      return Column(mainAxisSize: MainAxisSize.min, children: <Widget>[
        titleWidget,
        _buildRangeShowWidget(),
        datePickerWidget
      ]);
    }
    return datePickerWidget;
  }

  Widget _buildRangeShowWidget() {
    return Container(
      padding: EdgeInsets.symmetric(horizontal: 16.w, vertical: 14.w),
      color: Colors.white,
      child: Column(
        crossAxisAlignment: CrossAxisAlignment.start,
        children: [
          SizedBox(height: 5.w),
          Text(
            "自定义",
            style: TextStyle(
                fontSize: 14.sp,
                color: ColorManager.color353535,
                fontWeight: FontWeight.w600),
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
          ),
          SizedBox(height: 11.w),
          Row(
            children: [
              _buildRangeItemWidget(
                  isSelected: _rangeInitType == RangeInitType.start,
                  date: _getRangShowDateString(
                      _currStartYear, _currStartMonth, _currStartDay),
                  onClick: () {
                    setCurrValue(RangeInitType.start);
                  }),
              Padding(
                padding: EdgeInsets.symmetric(horizontal: 11.5.w),
                child: Text(
                  "至",
                  style: TextStyle(
                      fontSize: 14.sp, color: ColorManager.color353535),
                  maxLines: 1,
                  overflow: TextOverflow.ellipsis,
                ),
              ),
              _buildRangeItemWidget(
                  isSelected: _rangeInitType == RangeInitType.end,
                  date: _getRangShowDateString(
                      _currEndYear, _currEndMonth, _currEndDay),
                  onClick: () {
                    setCurrValue(RangeInitType.end);
                  })
            ],
          )
        ],
      ),
    );
  }

  Widget _buildRangeItemWidget(
      {bool isSelected = false, String? date, required VoidCallback onClick}) {
    return Expanded(
      child: GestureDetector(
        onTap: onClick,
        child: Container(
          height: 30.w,
          decoration: BoxDecoration(
              color: isSelected
                  ? ColorManager.colorFFF3F2
                  : ColorManager.colorF9F9F9,
              borderRadius: BorderRadius.circular(2.w),
              border: Border.all(
                  color: isSelected
                      ? ColorManager.colorF65247
                      : ColorManager.colorF9F9F9)),
          alignment: Alignment.center,
          child: Text(
            date ?? "",
            style: TextStyle(
                fontSize: 14.sp,
                color: isSelected
                    ? ColorManager.colorF65247
                    : ColorManager.colorCCCCCC,
                height: 1.2),
            maxLines: 1,
            overflow: TextOverflow.ellipsis,
          ),
        ),
      ),
    );
  }

  void setCurrValue(RangeInitType rangeInitType) {
    if (rangeInitType == RangeInitType.start) {
      _currYear = _currStartYear;
      _currMonth = _currStartMonth;
      _currDay = _currStartDay;
      _rangeInitType = RangeInitType.start;
    } else {
      _currYear = _currEndYear;
      _currMonth = _currEndMonth;
      _currDay = _currEndDay;
      _rangeInitType = RangeInitType.end;
    }
    _yearScrollCtrl.jumpToItem(_currYear - _yearRange.first);
    _monthScrollCtrl.jumpToItem(_currMonth - _monthRange.first);
    _dayScrollCtrl.jumpToItem(_currDay - _dayRange.first);
    setState(() {});
  }

  /// pressed cancel widget
  void _onPressedCancel() {
    if (widget.onCancel != null) {
      widget.onCancel!();
    }
    Navigator.pop(context);
  }

  /// pressed confirm widget
  void _onPressedConfirm() {
    if (widget.onConfirm != null) {
      DateTime startDateTime =
          DateTime(_currStartYear, _currStartMonth, _currStartDay);
      DateTime endDateTime = DateTime(_currEndYear, _currEndMonth, _currEndDay);
      if (startDateTime.compareTo(endDateTime) > 0) {
        ToastUtils.showToast("起始日期不得超过终止日期");
        return;
      }
      widget.onConfirm!(startDateTime, endDateTime, _calcStartSelectIndexList(),
          _calcEndSelectIndexList());
    }
    if (widget.canPop) Navigator.pop(context);
  }

  /// notify selected date changed
  void _onSelectedChange() {
    if (widget.onChange != null) {
      DateTime dateTime = DateTime(_currYear, _currMonth, _currDay);
      // widget.onChange!(dateTime, _calcSelectIndexList());
    }
  }

  /// find scroll controller by specified format
  FixedExtentScrollController _findScrollCtrl(String format) {
    late FixedExtentScrollController scrollCtrl;
    _scrollCtrlMap.forEach((key, value) {
      if (format.contains(key)) {
        scrollCtrl = value;
      }
    });
    return scrollCtrl;
  }

  /// find item value range by specified format
  List<int> _findPickerItemRange(String format) {
    late List<int> valueRange;
    _valueRangeMap.forEach((key, value) {
      if (format.contains(key)) {
        valueRange = value;
      }
    });
    return valueRange;
  }

  /// render the picker widget of year、month and day
  Widget _renderDatePickerWidget() {
    List<Widget> pickers = [];
    List<String> formatArr =
        DateTimeFormatter.splitDateFormat(widget.dateFormat);
    formatArr.forEach((format) {
      List<int> valueRange = _findPickerItemRange(format);

      Widget pickerColumn = _renderDatePickerColumnComponent(
        scrollCtrl: _findScrollCtrl(format),
        valueRange: valueRange,
        format: format,
        valueChanged: (value) {
          if (format.contains('y')) {
            _changeYearSelection(value);
          } else if (format.contains('M')) {
            _changeMonthSelection(value);
          } else if (format.contains('d')) {
            _changeDaySelection(value);
          }
        },
      );
      pickers.add(pickerColumn);
    });
    return Row(
        mainAxisAlignment: MainAxisAlignment.spaceBetween, children: pickers);
  }

  Widget _renderDatePickerColumnComponent({
    required FixedExtentScrollController scrollCtrl,
    required List<int> valueRange,
    required String format,
    required ValueChanged<int> valueChanged,
  }) {
    return Expanded(
      flex: 1,
      child: Container(
        height: widget.themeData.pickerHeight,
        decoration: BoxDecoration(color: widget.themeData.backgroundColor),
        child: BrnPicker.builder(
          backgroundColor: widget.themeData.backgroundColor,
          lineColor: widget.themeData.dividerColor,
          scrollController: scrollCtrl,
          itemExtent: widget.themeData.itemHeight,
          onSelectedItemChanged: valueChanged,
          childCount: valueRange.last - valueRange.first + 1,
          itemBuilder: (context, index) => _renderDatePickerItemComponent(
              format.contains("y")
                  ? ColumnType.Year
                  : (format.contains("M") ? ColumnType.Month : ColumnType.Day),
              index,
              valueRange.first + index,
              format),
        ),
      ),
    );
  }

  Widget _renderDatePickerItemComponent(
      ColumnType columnType, int index, int value, String format) {
    TextStyle textStyle = widget.themeData.itemTextStyle;
    if ((ColumnType.Year == columnType && index == _calcSelectIndexList()[0]) ||
        (ColumnType.Month == columnType &&
            index == _calcSelectIndexList()[1]) ||
        (ColumnType.Day == columnType && index == _calcSelectIndexList()[2])) {
      textStyle = widget.themeData.itemTextSelectedStyle;
    }
    return Container(
      height: widget.themeData.itemHeight,
      alignment: Alignment.center,
      child: Text(
          DateTimeFormatter.formatDateTime(value, format, widget.locale),
          style: textStyle),
    );
  }

  String _getRangShowDateString(int year, int month, int day) {
    List<String> formatArr =
        DateTimeFormatter.splitDateFormat(widget.dateFormat);
    String content = "";
    formatArr.forEach((item) {
      int count =
          item.contains("y") ? year : (item.contains("M") ? month : day);
      content += DateTimeFormatter.formatDateTime(count, item, widget.locale);
    });

    return content;
  }

  /// change the selection of year picker
  void _changeYearSelection(int index) {
    int year = _yearRange.first + index;
    if (_currYear != year) {
      _currYear = year;
      if (_rangeInitType == RangeInitType.start) {
        _currStartYear = _currYear;
      } else {
        _currEndYear = _currYear;
      }
      _changeDateRange();
      _onSelectedChange();
    }
  }

  /// change the selection of month picker
  void _changeMonthSelection(int index) {
    int month = _monthRange.first + index;
    if (_currMonth != month) {
      _currMonth = month;
      if (_rangeInitType == RangeInitType.start) {
        _currStartMonth = _currMonth;
      } else {
        _currEndMonth = _currMonth;
      }
      _changeDateRange();
      _onSelectedChange();
    }
  }

  /// change the selection of day picker
  void _changeDaySelection(int index) {
    int dayOfMonth = _dayRange.first + index;
    if (_currDay != dayOfMonth) {
      _currDay = dayOfMonth;
      if (_rangeInitType == RangeInitType.start) {
        _currStartDay = _currDay;
      } else {
        _currEndDay = _currDay;
      }
      _changeDateRange();
      _onSelectedChange();
    }
  }

  /// change range of month and day
  void _changeDateRange() {
    if (_isChangeDateRange) {
      return;
    }
    _isChangeDateRange = true;

    List<int> monthRange = _calcMonthRange();
    bool monthRangeChanged = _monthRange.first != monthRange.first ||
        _monthRange.last != monthRange.last;
    if (monthRangeChanged) {
      // selected year changed
      _currMonth = max(min(_currMonth, monthRange.last), monthRange.first);
    }

    List<int> dayRange = _calcDayRange();
    bool dayRangeChanged =
        _dayRange.first != dayRange.first || _dayRange.last != dayRange.last;
    if (dayRangeChanged) {
      // day range changed, need limit the value of selected day
      _currDay = max(min(_currDay, dayRange.last), dayRange.first);
    }

    setState(() {
      _monthRange = monthRange;
      _dayRange = dayRange;

      _valueRangeMap['M'] = monthRange;
      _valueRangeMap['d'] = dayRange;
    });

    if (monthRangeChanged) {
      // CupertinoPicker refresh data not working (https://github.com/flutter/flutter/issues/22999)
      int currMonth = _currMonth;
      _monthScrollCtrl.jumpToItem(monthRange.last - monthRange.first);
      if (currMonth < monthRange.last) {
        _monthScrollCtrl.jumpToItem(currMonth - monthRange.first);
      }
    }

    if (dayRangeChanged) {
      // CupertinoPicker refresh data not working (https://github.com/flutter/flutter/issues/22999)
      int currDay = _currDay;
      _dayScrollCtrl.jumpToItem(dayRange.last - dayRange.first);
      if (currDay < dayRange.last) {
        _dayScrollCtrl.jumpToItem(currDay - dayRange.first);
      }
    }

    _isChangeDateRange = false;
  }

  /// calculate the count of day in current month
  int _calcDayCountOfMonth() {
    if (_currMonth == 2) {
      return isLeapYear(_currYear) ? 29 : 28;
    } else if (_solarMonthsOf31Days.contains(_currMonth)) {
      return 31;
    }
    return 30;
  }

  /// whether or not is leap year
  bool isLeapYear(int year) {
    return (year % 4 == 0 && year % 100 != 0) || year % 400 == 0;
  }

  /// calculate selected index list
  List<int> _calcSelectIndexList() {
    int yearIndex = _currYear - _minDateTime.year;
    int monthIndex = _currMonth - _monthRange.first;
    int dayIndex = _currDay - _dayRange.first;
    return [yearIndex, monthIndex, dayIndex];
  }

  /// calculate selected index list
  List<int> _calcStartSelectIndexList() {
    int yearIndex = _currStartYear - _minDateTime.year;
    int monthIndex = _currStartMonth - _monthRange.first;
    int dayIndex = _currDay - _dayRange.first;
    return [yearIndex, monthIndex, dayIndex];
  }

  /// calculate selected index list
  List<int> _calcEndSelectIndexList() {
    int yearIndex = _currEndYear - _minDateTime.year;
    int monthIndex = _currEndMonth - _monthRange.first;
    int dayIndex = _currEndDay - _dayRange.first;
    return [yearIndex, monthIndex, dayIndex];
  }

  /// calculate the range of year
  List<int> _calcYearRange() {
    return [_minDateTime.year, _maxDateTime.year];
  }

  /// calculate the range of month
  List<int> _calcMonthRange() {
    int minMonth = 1, maxMonth = 12;
    int minYear = _minDateTime.year;
    int maxYear = _maxDateTime.year;
    if (minYear == _currYear) {
      // selected minimum year, limit month range
      minMonth = _minDateTime.month;
    }
    if (maxYear == _currYear) {
      // selected maximum year, limit month range
      maxMonth = _maxDateTime.month;
    }
    return [minMonth, maxMonth];
  }

  /// calculate the range of day
  List<int> _calcDayRange({currMonth}) {
    int minDay = 1, maxDay = _calcDayCountOfMonth();
    int minYear = _minDateTime.year;
    int maxYear = _maxDateTime.year;
    int minMonth = _minDateTime.month;
    int maxMonth = _maxDateTime.month;
    if (currMonth == null) {
      currMonth = _currMonth;
    }
    if (minYear == _currYear && minMonth == currMonth) {
      // selected minimum year and month, limit day range
      minDay = _minDateTime.day;
    }
    if (maxYear == _currYear && maxMonth == currMonth) {
      // selected maximum year and month, limit day range
      maxDay = _maxDateTime.day;
    }
    return [minDay, maxDay];
  }
}
